#include "stdafx.h"
#include <intrin.h>
#include "primitive_datatypes.h"
#include "CPUInfo.h"
//##########################################################
CCPUInfo::CCPUInfo(void) 
{
	memset(this,0,sizeof(CCPUInfo));
}
//##########################################################
void CCPUInfo::GetInfoASM(void){
	memset(this,0,sizeof(CCPUInfo));

	Char* pVendor= this->_vendor;
	Char* pName = this->_name;
	Bool SSE	= false;
	Bool SSE2	= false;
	Bool SSE3	= false;
	Bool SSSE3	= false;
	Bool SSE41	= false;
	Bool SSE42	= false;
	Bool SSE4A	= false;
	Bool MMX	= false;
	Bool MMXExt	= false;
	Bool D3Now	= false;
	Bool D3NowExt=false;

	__try{
		_asm{
			mov eax, 0  //get vendor name
			CPUID

			mov esi, pVendor
			mov [esi], ebx
			mov [esi+4], edx
			mov [esi+8], ecx
			
			mov eax, 1
			CPUID

			test edx, 00800000h // 23 - mmx
			jz _NOMMX
			mov MMX, 1

_NOMMX:		test edx, 02000000h // 25 - sse
			jz _NOSSE
			mov SSE, 1

_NOSSE:		test edx, 04000000h // 26 - sse2
			jz _NOSSE2
			mov SSE2, 1

_NOSSE2:	test ecx, 00000001h // 0
			jz _NOSSE3
			mov SSE3, 1

_NOSSE3:	test ecx, 00000200h // 9
			js _NOSSSE3
			mov SSSE3, 1

_NOSSSE3:	test ecx, 00080000h // 19
			jz _NOSSE41
			mov SSE41, 1

_NOSSE41:	test ecx, 00100000h // 20
			jz _NOSSE42
			mov SSE42, 1

_NOSSE42:	mov eax, 80000000h
			CPUID

			cmp eax, 80000000h
			jbe _EXIT

			mov eax, 80000001h
			CPUID

			test edx, 80000000h
			jz _NO3DNOW
			mov D3Now, 1

_NO3DNOW:	test edx, 40000000h
			jz _NO3DNOWEXT
			mov D3NowExt, 1

_NO3DNOWEXT: test edx, 00400000h
			 jz _NOMMXEXT
			 mov MMXExt, 1

_NOMMXEXT:	test ecx, 00000040h
			jz _NOSSE4A
			mov SSE4A, 1

_NOSSE4A:	mov eax, 80000002h
			CPUID
			mov esi, pName
			mov [esi], eax
			mov [esi+4], ebx
			mov [esi+8], ecx
			mov [esi+12], edx
			mov eax, 80000003h
			CPUID
			mov [esi+16], eax
			mov [esi+20], ebx
			mov [esi+24], ecx
			mov [esi+28], edx
			mov eax, 80000004h
			CPUID
			mov [esi+32], eax
			mov [esi+36], ebx
			mov [esi+40], ecx
			mov [esi+44], edx
_EXIT:		
		}
	}__except(EXCEPTION_EXECUTE_HANDLER){
		if(_exception_code() == STATUS_ILLEGAL_INSTRUCTION)
			return;
		return;
	}

	this->_SSE = SSE;
	this->_SSE2 = SSE2;
	this->_SSE3 = SSE3;
	this->_SSSE3 = SSSE3;
	this->_SSE41 = SSE41;
	this->_SSE42 = SSE42;
	this->_SSE4A = SSE4A;
	this->_MMX = MMX;
	this->_MMXExt = MMXExt;
	this->_3DNow = D3Now;
	this->_3DNowExt = D3NowExt;
}
//##########################################################
void CCPUInfo::GetInfoIntrinsics(void){
	memset(this,0,sizeof(CCPUInfo));

	Int info[4];

	__cpuid(info,0);
	memcpy(this->_vendor,info+1,sizeof(Char)*12);

	__cpuid(info,1);	
	(info[3] & 0x00800000) ? this->_MMX = true : this->_MMX = false ;
	(info[3] & 0x02000000)  ? this->_SSE =true : this->_SSE = false;
	(info[3] & 0x04000000)  ? this->_SSE2 =true : this->_SSE2 = false;
	(info[2] & 0x1)		    ? this->_SSE3 =true : this->_SSE3 = false;
	(info[2] & 0x00000200) ? this->_SSSE3 =true : this->_SSSE3 = false;
	(info[2] & 0x00080000) ? this->_SSE41 =true : this->_SSE41 = false;
	(info[2] & 0x00100000) ? this->_SSE42 =true : this->_SSE42 = false;

	__cpuid(info,0x80000001);
	(info[2] & 0x40)		? this->_SSE4A = true : this->_SSE4A = false;
	(info[3] & 0x80000000)? this->_3DNow = true : this->_3DNow = false;
	(info[3] & 0x40000000)? this->_3DNowExt = true : this->_3DNowExt = false;

	__cpuid(info,0x80000002);
	memcpy(this->_name,info,sizeof(char) * 16);
	__cpuid(info,0x80000003);
	memcpy(this->_name+16,info,sizeof(char) * 16);
	__cpuid(info,0x80000004);
	memcpy(this->_name+32,info,sizeof(char) * 16);

}
//##########################################################
void CCPUInfo::Print(void){
	std::cout << "CPU name:" << this->_name << "  vendor:" << this->_vendor << std::endl;
	std::cout << "Features:" << std::endl;
	if(this->_3DNow) std::cout << "3DNow!" << std::endl;
	if(this->_3DNowExt) std::cout << "3DNow!+" << std::endl;
	if(this->_MMX) std::cout << "MMX" << std::endl;
	if(this->_MMXExt) std::cout << "MMX+" << std::endl;
	if(this->_SSE) std::cout << "SSE" << std::endl;
	if(this->_SSE2) std::cout <<  "SSE2" << std::endl;
	if(this->_SSE3) std::cout <<  "SSE3" << std::endl;
	if(this->_SSSE3) std::cout << "SSSE3" << std::endl;
	if(this->_SSE4A) std::cout << "SSE4A" << std::endl;
	if(this->_SSE41) std::cout << "SSE41" << std::endl;
	if(this->_SSE42) std::cout << "SSE42" << std::endl;
}
//##########################################################
String CCPUInfo::GetShortString(void){
	String ret = "CPU: ";
	ret += this->_name;
	ret += " - ";
	if(this->_3DNow)	ret += "3DNow! ";
	if(this->_3DNowExt) ret += "3DNow!+ ";
	if(this->_MMX)		ret += "MMX ";
	if(this->_MMXExt)	ret += "MMX+";
	if(this->_SSE)		ret += "SSE ";
	if(this->_SSE2)		ret += "SSE2 "; 
	if(this->_SSE3)		ret += "SSE3 " ;
	if(this->_SSSE3)	ret += "SSSE3 ";
	if(this->_SSE4A)	ret += "SSE4A ";
	if(this->_SSE41)	ret += "SSE41 ";
	if(this->_SSE42)	ret += "SSE42 ";
	return ret;
}
//##########################################################
Bool CCPUInfo::OSSupportsSSE(void){
	__try{
		_asm{
			xorps xmm0, xmm0
		}
	}
	__except(EXCEPTION_EXECUTE_HANDLER){
		if(_exception_code() == STATUS_ILLEGAL_INSTRUCTION)
			return false; // sse not supported
		return false; // other error
	}

	return true;
}
//##########################################################
//##########################################################
//##########################################################
//##########################################################
//##########################################################
//##########################################################
//##########################################################
//##########################################################
//##########################################################
//##########################################################